package com.softWang.system.service;

import com.baomidou.mybatisplus.core.assist.ISqlRunner;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.softWang.common.base.BaseServiceMy;
import com.softWang.common.base.LoginUser;
import com.softWang.common.constants.CacheKeys;
import com.softWang.common.constants.CommonStateEnum;
import com.softWang.common.dataPermission.rule.dept.DeptDataPermissionRespDTO;
import com.softWang.common.exception.LogicException;
import com.softWang.common.service.CacheService;
import com.softWang.common.utils.IPUtils;
import com.softWang.common.utils.SecurityUtils;
import com.softWang.common.utils.WebUtils;
import com.softWang.system.constants.DataScopeEnum;
import com.softWang.system.constants.SystemParamterContants;
import com.softWang.system.controller.vo.systemAuth.*;
import com.softWang.system.mapper.*;
import com.softWang.system.po.*;
import org.apache.commons.collections4.SetUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.softWang.common.dataPermission.rule.dept.DeptDataPermissionRule.DEPT_DATA_PERMISSION_KEY;

@Service
public class SystemAuthService extends BaseServiceMy<SystemUsers> {
    @Resource
    private SystemUsersMapper systemUsersMapper;
    @Resource
    private SystemParameterService systemParameterService;
    @Resource
    private SystemUserRoleMapper systemUserRoleMapper;
    @Resource
    private SystemRolesMapper systemRolesMapper;
    @Resource
    private SystemDeptsMapper systemDeptsMapper;
    @Resource
    private SystemRoleMenuMapper systemRoleMenuMapper;
    @Resource
    private SystemMenuMapper systemMenuMapper;
    @Resource
    private SystemLoginLogMapper systemLoginLogMapper;

    @Resource
    private SystemTenantMapper systemTenantMapper;
    @Resource
    private SystemTenantMenuMapper systemTenantMenuMapper;

    @Resource
    private CacheService cacheService;

    @Value("${token.accessToken.expireTime}")
    private Long expireTimeAccess;

    @Value("${token.refreshToken.expireTime}")
    private Long expireTimeRefresh;

    @Value("${token.header}")
    private String tokenHeader;

    /** 所有权限标识 */
    private static final String ALL_PERMISSION = "*:*:*";

    /** 管理员角色权限标识 */
    private static final String SUPER_ADMIN = "superAdmin";
    @Resource
    private SystemRoleDeptMapper systemRoleDeptMapper;

    /**
     * 账号密码登录
     */
    public LoginResult login(LoginParam param, HttpServletRequest request) {

        SystemUsers user = systemUsersMapper.selectOne(Wrappers.lambdaQuery(SystemUsers.class)
               .eq(SystemUsers::getLoginName, param.getLoginName()));
        if (user == null) {
            throw new LogicException("用户不存在:"+param.getLoginName());
        }

        String password = user.getPassword();
        String verifyPassword = param.getPassword().toUpperCase();
        if (!password.equals(verifyPassword)) {
            throw new LogicException("密码错误");
        }
        if (user.getState() == 1) {
            throw new LogicException("用户已被禁用");
        }

        //判断租户是否存在且状态正常
        SystemTenant tenant = systemTenantMapper.selectById(user.getTenantId());
        if(tenant == null || tenant.getState() != CommonStateEnum.NORMAL.getCode()){
            throw new LogicException("租户不存在或状态异常");
        }

        String accessToken = UUID.randomUUID().toString().replaceAll("-", "");
        String refreshToken = UUID.randomUUID().toString().replaceAll("-", "");

        LoginUser loginUser = new LoginUser();
        loginUser.setUserId(user.getId());
        loginUser.setType(user.getType());
        loginUser.setUserName(user.getUname());
        loginUser.setLoginName(param.getLoginName());
        loginUser.setDeptId(user.getDeptid());
        loginUser.setUserIp(IPUtils.getIpAddr(request));
        if(user.getType() == SystemUsers.UserType.SUPPERADMIN.getCode()){
            //超管拥有所有权限和角色权限
            loginUser.getPermissions().add(ALL_PERMISSION);
            loginUser.getRoleCodes().add(SUPER_ADMIN);
        }else if(user.getType() == SystemUsers.UserType.TENANT_ADMIN.getCode()){
            //租户管理员拥有租户的所有权限
            loginUser.setPermissions(getPermissionsByTenantId(user.getTenantId()));
        }else{
            //普通用户拥有角色权限
            List<Long> roleIds = getRoleIds(user.getId());
            loginUser.setPermissions(getPermissions(roleIds));
            loginUser.setRoleCodes(getRoleCodes(roleIds));
        }
        loginUser.setTenantId(user.getTenantId());

        //查询数据权限
        DeptDataPermissionRespDTO deptDataPermission = getDeptDataPermission(user);
        // 添加到上下文中，避免重复计算
        loginUser.setContext(DEPT_DATA_PERMISSION_KEY, deptDataPermission);

        // 生成token并存入redis
        LoginUser oldUser = cacheService.getCacheObject(CacheKeys.LOGIN_USER_PREFIX + user.getId());
        if(oldUser != null){
            Map<String,String> oldClientInfos = oldUser.getClientInfos().get(param.getClientType());
            if(oldClientInfos != null ){
                //清除旧的token
                cacheService.deleteObject(CacheKeys.LOGIN_ACCESS_TOKEN_PREFIX + oldClientInfos.get("accessToken"));
                cacheService.deleteObject(CacheKeys.LOGIN_REFRESH_TOKEN_PREFIX + oldClientInfos.get("refreshToken"));
                oldUser.getClientInfos().remove(param.getClientType());
            }
            loginUser.setClientInfos(oldUser.getClientInfos());
        }else{
            loginUser.setClientInfos(new HashMap<>());
        }
        //将新token存入
        Map<String,String> tokenInfo = new HashMap<>();
        tokenInfo.put("accessToken",accessToken);
        tokenInfo.put("refreshToken",refreshToken);
        loginUser.getClientInfos().put(param.getClientType(),tokenInfo);

        cacheService.setCacheObject(CacheKeys.LOGIN_USER_PREFIX + user.getId(), loginUser, expireTimeRefresh, TimeUnit.SECONDS);
        cacheService.setCacheObject(CacheKeys.LOGIN_ACCESS_TOKEN_PREFIX + accessToken, user.getId(),  expireTimeAccess, TimeUnit.SECONDS);
        cacheService.setCacheObject(CacheKeys.LOGIN_REFRESH_TOKEN_PREFIX + refreshToken, user.getId(), expireTimeRefresh, TimeUnit.SECONDS);

        //记录登录日志
        createLoginLog(user,loginUser,1);

        LoginResult result = new LoginResult();
        result.setId(user.getId());
        result.setAccessToken(accessToken);
        result.setRefreshToken(refreshToken);
        result.setExpiresTime(LocalDateTime.now().plusSeconds(expireTimeAccess));
        //判断用户密码是否过期
         SystemParameter passwordValidP = systemParameterService.getByKey(SystemParamterContants.USER_PASS_VALID_ENABLE);
        if(passwordValidP==null){
            throw new LogicException("无法获得参数:"+SystemParamterContants.USER_PASS_VALID_ENABLE);
        }
        if(passwordValidP.getPvalue().equals("0")){//未启用密码过期校验
            result.setPassExpired("0");
        }else{
            LocalDateTime passValidDate = user.getPassValidDate();
            if(passValidDate != null){
                LocalDateTime now = LocalDateTime.now();
                if(now.isAfter(passValidDate)){
                    result.setPassExpired("1");
                }else{
                    result.setPassExpired("0");
                }
            }else{
                result.setPassExpired("1");
            }
        }
        return result;
    }

    /**
     * 退出登录
     */
    public void logout(Integer clientType,HttpServletRequest request) {
        String accessToken = WebUtils.getHeader(request,tokenHeader);

        if (accessToken != null && accessToken.length() > 0) {
            Long userId = Long.valueOf(cacheService.getCacheObject(CacheKeys.LOGIN_ACCESS_TOKEN_PREFIX + accessToken).toString());
            if (userId != null) {
                LoginUser loginUser = cacheService.getCacheObject(CacheKeys.LOGIN_USER_PREFIX + userId);
                if (loginUser != null) {
                    loginUser.setUserIp(IPUtils.getIpAddr(request));
                    Map<String, String> oldClientInfos = loginUser.getClientInfos().get(clientType);
                    if (oldClientInfos != null) {
                        //清除旧的token
                        cacheService.deleteObject(CacheKeys.LOGIN_ACCESS_TOKEN_PREFIX + oldClientInfos.get("accessToken"));
                        cacheService.deleteObject(CacheKeys.LOGIN_REFRESH_TOKEN_PREFIX + oldClientInfos.get("refreshToken"));
                        loginUser.getClientInfos().remove(clientType);
                        cacheService.setCacheObject(CacheKeys.LOGIN_USER_PREFIX + userId, loginUser, expireTimeRefresh, TimeUnit.SECONDS);
                    }

                    //记录登录日志
                    SystemUsers user = systemUsersMapper.selectById(userId);
                    createLoginLog(user,loginUser,2);
                }
            }
        }

    }

    /**
     * 刷新token
     */
    public RefreshTokenResult refreshToken(RefreshTokenParam param, HttpServletRequest request) {

        String accessToken = UUID.randomUUID().toString().replaceAll("-", "");
        String refreshToken = UUID.randomUUID().toString().replaceAll("-", "");
        Long userId =null;
        Object userIdObj = cacheService.getCacheObject(CacheKeys.LOGIN_REFRESH_TOKEN_PREFIX + param.getRefreshToken());
        if (userIdObj != null) {
            userId = Long.valueOf(userIdObj.toString());
            LoginUser loginUser = cacheService.getCacheObject(CacheKeys.LOGIN_USER_PREFIX + userId);
            if (loginUser != null) {
                loginUser.setUserIp(IPUtils.getIpAddr(request));
                Map<String, String> oldClientInfos = loginUser.getClientInfos().get(param.getClientType());
                if (oldClientInfos != null) {
                    //清除旧的token
                    cacheService.deleteObject(CacheKeys.LOGIN_ACCESS_TOKEN_PREFIX + oldClientInfos.get("accessToken"));
                    cacheService.deleteObject(CacheKeys.LOGIN_REFRESH_TOKEN_PREFIX + oldClientInfos.get("refreshToken"));
                    loginUser.getClientInfos().remove(param.getClientType());
                }
                //将新token存入
                Map<String,String> tokenInfo = new HashMap<>();
                tokenInfo.put("accessToken",accessToken);
                tokenInfo.put("refreshToken",refreshToken);
                loginUser.getClientInfos().put(param.getClientType(),tokenInfo);

                cacheService.setCacheObject(CacheKeys.LOGIN_USER_PREFIX + userId, loginUser, expireTimeRefresh, TimeUnit.SECONDS);
                cacheService.setCacheObject(CacheKeys.LOGIN_ACCESS_TOKEN_PREFIX + accessToken, userId,  expireTimeAccess, TimeUnit.SECONDS);
                cacheService.setCacheObject(CacheKeys.LOGIN_REFRESH_TOKEN_PREFIX + refreshToken, userId, expireTimeRefresh, TimeUnit.SECONDS);
            }else{
                throw new LogicException("refreshToken已失效");
            }
        }else{
            throw new LogicException("refreshToken已失效");
        }

        RefreshTokenResult result = new RefreshTokenResult();
        result.setId(userId);
        result.setAccessToken(accessToken);
        result.setRefreshToken(refreshToken);
        result.setExpiresTime(LocalDateTime.now().plusSeconds(expireTimeAccess));
        return result;
    }

    private void createLoginLog(SystemUsers user,LoginUser loginUser,Integer type) {
        SystemLoginLog log = new SystemLoginLog(null,type, user.getId(), user.getUname(),user.getLoginName(),LocalDateTime.now(),loginUser.getUserIp(),loginUser.getTenantId());
        systemLoginLogMapper.insert(log);
    }


    private List<Long> getRoleIds(Long userId) {
        List<Long> result = new ArrayList<>();
        //获取角色
        List<SystemUserRole> userRoles = systemUserRoleMapper.selectList(Wrappers.lambdaQuery(SystemUserRole.class)
                .eq(SystemUserRole::getUserid, userId));
        if(userRoles != null && userRoles.size() > 0){
            result = userRoles.stream().map(SystemUserRole::getRoleid).collect(Collectors.toList());
        }
        return result;
    }

    private Set<String> getRoleCodes(List<Long> roleIds) {
        Set<String> result = new HashSet<>();
        if(roleIds != null && roleIds.size() > 0){
            List<SystemRoles> roles = systemRolesMapper.selectBatchIds(roleIds);
            result = roles.stream().map(SystemRoles::getCode).collect(Collectors.toSet());
        }
        return result;
    }

    private Set<String> getPermissions(List<Long> roleIds) {
        Set<String> result = new HashSet<>();
        if(roleIds != null && roleIds.size() > 0){
            List<SystemRoleMenu> roleMenus = systemRoleMenuMapper.selectList(Wrappers.lambdaQuery(SystemRoleMenu.class)
                   .in(SystemRoleMenu::getRoleid, roleIds));
            if(roleMenus != null && roleMenus.size() > 0){
                List<Long> menuIds = roleMenus.stream().map(SystemRoleMenu::getMenuid).collect(Collectors.toList());
                List<SystemMenu> menus = systemMenuMapper.selectList(Wrappers.lambdaQuery(SystemMenu.class)
                        .in(SystemMenu::getId, menuIds)
                        .eq(SystemMenu::getState, SystemMenu.State.NORMAL.getCode())
                        .isNotNull(SystemMenu::getPermission));
                result = menus.stream().map(SystemMenu::getPermission).collect(Collectors.toSet());
            }
        }
        return result;
    }

    private Set<String> getPermissionsByTenantId(Long tenantId) {
        Set<String> result = new HashSet<>();

        List<SystemTenantMenu> tenantMenus = systemTenantMenuMapper.selectList(Wrappers.lambdaQuery(SystemTenantMenu.class)
                .eq(SystemTenantMenu::getTenantId, tenantId));
        if(tenantMenus == null || tenantMenus.isEmpty()){
            // 租户菜单为空，返回空列表
            return result;
        }

        List<Long> menuIds = tenantMenus.stream()
                .map(SystemTenantMenu::getMenuId)
                .distinct()
                .collect(Collectors.toList());
        List<SystemMenu> menus = systemMenuMapper.selectList(Wrappers.lambdaQuery(SystemMenu.class)
                .in(SystemMenu::getId, menuIds)
                .eq(SystemMenu::getState, SystemMenu.State.NORMAL.getCode())
                .isNotNull(SystemMenu::getPermission));

        result = menus.stream().map(SystemMenu::getPermission).collect(Collectors.toSet());
        return result;
    }

    public UserInfoResult userInfo() {
        UserInfoResult result = new UserInfoResult();

        LoginUser loginUser = SecurityUtils.getLoginUser();
        Long userId = loginUser.getUserId();

        SystemUsers user = systemUsersMapper.selectById(userId);
        BeanUtils.copyProperties(user, result);

        result.setPermissions(loginUser.getPermissions());
        result.setRoleCodes(loginUser.getRoleCodes());

        List<SystemMenu> menus = new ArrayList<>();
        if(user.getType() == SystemUsers.UserType.SUPPERADMIN.getCode()){
            //如果时超管，得到所有菜单
            menus = systemMenuMapper.selectList(Wrappers.lambdaQuery(SystemMenu.class)
                    .in(SystemMenu::getType, Arrays.asList(1,2))
                    .eq(SystemMenu::getState, SystemMenu.State.NORMAL.getCode())
                    .orderByAsc(SystemMenu::getSort));
        } else if(user.getType() == SystemUsers.UserType.TENANT_ADMIN.getCode()){
            //如果是租户管理员，得到租户的所有菜单
            List<SystemTenantMenu> tenantMenus = systemTenantMenuMapper.selectList(Wrappers.lambdaQuery(SystemTenantMenu.class)
                    .eq(SystemTenantMenu::getTenantId, loginUser.getTenantId()));
            if(tenantMenus != null && !tenantMenus.isEmpty()){
                List<Long> menuIds = tenantMenus.stream()
                        .map(SystemTenantMenu::getMenuId)
                        .distinct()
                        .collect(Collectors.toList());
                menus = systemMenuMapper.selectList(Wrappers.lambdaQuery(SystemMenu.class)
                        .in(SystemMenu::getId, menuIds)
                        .in(SystemMenu::getType, Arrays.asList(1,2))
                        .eq(SystemMenu::getState, SystemMenu.State.NORMAL.getCode())
                        .orderByAsc(SystemMenu::getSort));
            }
        }else{
            //如果不是超管也不是租户管理员，得到角色对应的菜单
            List<Long> roleIds = getRoleIds(userId);
            if(roleIds != null && roleIds.size() > 0){
                List<SystemRoleMenu> roleMenus = systemRoleMenuMapper.selectList(Wrappers.lambdaQuery(SystemRoleMenu.class)
                        .in(SystemRoleMenu::getRoleid, roleIds));
                if(roleMenus != null && roleMenus.size() > 0){
                    List<Long> menuIds = roleMenus.stream().map(SystemRoleMenu::getMenuid).collect(Collectors.toList());
                    menus = systemMenuMapper.selectList(Wrappers.lambdaQuery(SystemMenu.class)
                            .in(SystemMenu::getId, menuIds)
                            .in(SystemMenu::getType, Arrays.asList(1,2))
                            .eq(SystemMenu::getState, SystemMenu.State.NORMAL.getCode())
                            .orderByAsc(SystemMenu::getSort));
                }
            }
        }

        new UserInfoResult.MenuInfo();
        List<UserInfoResult.MenuInfo> menuInfoList = ConvertSystemUserMapper.INSTANCE.toMemuInfo(menus);
        result.setMenuList(menuInfoList);

        return result;
    }


    private DeptDataPermissionRespDTO getDeptDataPermission(SystemUsers user) {
        DeptDataPermissionRespDTO dto = new DeptDataPermissionRespDTO();
        // 判断用户是否是超管或租户管理员
        if (user.getType() == SystemUsers.UserType.SUPPERADMIN.getCode() ||
                user.getType() == SystemUsers.UserType.TENANT_ADMIN.getCode()) {
            dto.setAll(true);
            return dto;    // 直接返回
        }
        // 获得用户的角色
        List<SystemUserRole> userRoles = systemUserRoleMapper.selectList(Wrappers.<SystemUserRole>lambdaQuery().
                eq(SystemUserRole::getUserid, user.getId()));
        if (userRoles == null || userRoles.isEmpty()) {
            dto.setAll(false);
            dto.setSelf(true);
            return dto;
        }

        List<Long> roleIds = userRoles.stream().map(SystemUserRole::getRoleid).distinct().collect(Collectors.toList());
        // 获得角色
        List<SystemRoles> systemRoles = systemRolesMapper.selectBatchIds(roleIds);
        Set<Long> deptIds = new HashSet<>();
        for(SystemRoles role : systemRoles){
            if (role.getDataScope() == null) {
                continue;
            }
            // 情况一，ALL
            if (role.getDataScope().equals(DataScopeEnum.ALL.getCode())) {
                dto.setAll(true);
                break;
            }
            // 情况二，本部门
            if (role.getDataScope().equals(DataScopeEnum.DEPT.getCode())) {
                //查询用户的部门id
                deptIds.add(user.getDeptid());
                continue;
            }
            // 情况三，本部门及下属部门
            if (role.getDataScope().equals(DataScopeEnum.DEPT_AND_CHILD.getCode())) {
                //查询用户的部门id
                deptIds.add(user.getDeptid());
                //查询下属部门
                SystemDepts systemDepts = systemDeptsMapper.selectById(user.getDeptid());
                String treeCode = systemDepts.getTreecode();
                List<SystemDepts> deptList = systemDeptsMapper.selectList(Wrappers.<SystemDepts>lambdaQuery().
                        likeRight(SystemDepts::getTreecode, treeCode + "%"));
                if(deptList!= null && deptList.size() > 0){
                    deptIds.addAll(deptList.stream().map(SystemDepts::getId).collect(Collectors.toList()));
                }
                continue;
            }
            // 情况四，指定部门
            if (role.getDataScope().equals(DataScopeEnum.CUSTOM.getCode())) {
                //查询角色的自定义部门
                List<SystemRoleDept> systemRoleDepts = systemRoleDeptMapper.selectList(Wrappers.<SystemRoleDept>lambdaQuery().
                        eq(SystemRoleDept::getRoleid, role.getId()));
                if (systemRoleDepts != null && systemRoleDepts.size() > 0) {
                    deptIds.addAll(systemRoleDepts.stream().map(SystemRoleDept::getDeptId).collect(Collectors.toList()));
                }
                continue;
            }
            // 情况五，指定部门
            if (role.getDataScope().equals(DataScopeEnum.CUSTOM_AND_CHILD.getCode())) {
                //查询角色的自定义部门
                List<SystemRoleDept> systemRoleDepts = systemRoleDeptMapper.selectList(Wrappers.<SystemRoleDept>lambdaQuery().
                        eq(SystemRoleDept::getRoleid, role.getId()));
                if (systemRoleDepts != null && systemRoleDepts.size() > 0) {
                    deptIds.addAll(systemRoleDepts.stream().map(SystemRoleDept::getDeptId).collect(Collectors.toList()));
                }
                //查询下属部门
                for(SystemRoleDept roleDept : systemRoleDepts) {
                    SystemDepts systemDepts = systemDeptsMapper.selectById(roleDept.getDeptId());
                    String treeCode = systemDepts.getTreecode();
                    List<SystemDepts> deptList = systemDeptsMapper.selectList(Wrappers.<SystemDepts>lambdaQuery().
                            likeRight(SystemDepts::getTreecode, treeCode + "%"));
                    if (deptList != null && deptList.size() > 0) {
                        deptIds.addAll(deptList.stream().map(SystemDepts::getId).collect(Collectors.toList()));
                    }
                }
                continue;
            }
            // 情况六，SELF
            if (role.getDataScope().equals(DataScopeEnum.SELF.getCode())) {
                dto.setSelf(true);
                continue;
            }
        }
        dto.setDeptIds(deptIds);
        return dto;
    }
}
